package com.mrbug.pulltodismisspager;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Point;
import ohos.agp.utils.Rect;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayAttributes;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;

import java.math.BigDecimal;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 自定义封装类
 */
public class PullToDismissPager extends ComponentContainer {
    /**
     * 监听对象
     */
    private static final int CLOSE_BACKGROUND_COLOR = 0xfffffff;
    private static final int OPEN_BACKGROUND_COLOR = 0xff000000;
    private static final int FLING_OFFSET = 30;
    private static final int FLING_TIME = 100;
    private static final float MIDDLE_Y = 1000;
    private StateChangeLisener stateChangeLisener;
    private PageSlider mViewPager;
    private Timer timer;
    private ShapeElement shapeElement;
    private AnimatorValue animator;
    private float xFloat;
    private float yFloat;
    private boolean intercept = false;
    private boolean opened = false;
    private int minY = 0;
    private int maxY;
    private float startY;
    private int offsetAuto;
    private int offsetHeight;
    private long downTimeMillis;
    private boolean isFling;
    private long endTimeMillis;
    private float upDateOffset;
    private float lastOffset;
    private boolean isFirstTime = true;

    /**
     * 构造方法
     *
     * @param context 上下文
     */
    public PullToDismissPager(Context context) {
        this(context, null);
    }

    /**
     * 构造方法
     *
     * @param context 上下文
     * @param attrSet 资源文件
     */
    public PullToDismissPager(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public PullToDismissPager(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        DisplayAttributes attributes = display.getAttributes();
        maxY = attributes.height;
        mViewPager = new PageSlider(context);
        shapeElement = new ShapeElement();
        shapeElement.setRgbColor(RgbColor.fromArgbInt(CLOSE_BACKGROUND_COLOR));
        setBackground(shapeElement);
        shapeElement.setAlpha(1);
        setDraggedListener(Component.DRAG_VERTICAL, listener);
    }

    private DraggedListener listener = new DraggedListener() {
        @Override
        public void onDragDown(Component component, DragInfo dragInfo) {
            Point point = dragInfo.downPoint;
            int pX = point.getPointXToInt();
            int pY = point.getPointYToInt();
            downTimeMillis = System.currentTimeMillis();
            Rect rect = mViewPager.getComponentPosition();
            float[] floats = mViewPager.getContentPosition();
            xFloat = floats[0];
            yFloat = floats[1];
            intercept = xFloat < pX && pX < (double) xFloat + (rect.right - rect.left)
                    && yFloat < pY && pY < (double) yFloat + (rect.bottom - rect.top);
            if (intercept && animator != null) {
                animator.stop();
            }
            if (yFloat > MIDDLE_Y) {
                startY = maxY;
            } else {
                startY = minY;
            }
        }

        @Override
        public void onDragStart(Component component, DragInfo dragInfo) {
            // dragInfo.yOffset 永远等于0;
            yFloat = new BigDecimal((double) yFloat + dragInfo.yOffset).floatValue();
            mViewPager.setContentPosition(xFloat, yFloat);
            timer = new Timer();
            MyTask task = new MyTask();
            timer.schedule(task, 0, FLING_TIME);
        }

        @Override
        public void onDragUpdate(Component component, DragInfo dragInfo) {
            yFloat = new BigDecimal((double) yFloat + dragInfo.yOffset).floatValue();
            if (yFloat > maxY) {
                yFloat = maxY;
                return;
            }
            if (yFloat < minY) {
                yFloat = minY;
                return;
            }
            upDateOffset = yFloat;
            if (isFirstTime) {
                lastOffset = upDateOffset;
                isFirstTime = false;
            }
            mViewPager.setContentPosition(xFloat, yFloat);
            float alpha = new BigDecimal(0xC7 * Math.pow(((double) maxY
                    - (double) yFloat) / offsetHeight, 1)).floatValue();
            shapeElement.setAlpha((int) alpha);
        }

        @Override
        public void onDragEnd(Component component, DragInfo dragInfo) {
            timer.cancel();
            float offsetY = new BigDecimal((double) yFloat - (double) startY).floatValue();
            endTimeMillis = System.currentTimeMillis();
            if (endTimeMillis - downTimeMillis < FLING_TIME && offsetY > FLING_OFFSET) {
                isFling = true;
                collapsePanel();
                return;
            }
            if ((double) upDateOffset - (double) lastOffset > FLING_OFFSET) {
                isFling = false;
                collapsePanel();
                return;
            }
            isFling = false;
            // 向下滑动
            if (offsetY > 0 && offsetY > offsetAuto) {
                if (yFloat < maxY) {
                    startAnimator(false, yFloat, maxY);
                } else {
                    opened = false;
                }

            } else if (offsetY < 0 && -offsetY > offsetAuto) {
                // 向上滑动
                if (yFloat > minY) {
                    startAnimator(true, yFloat, minY);
                } else {
                    opened = true;
                }
            } else {
                // 恢复动画
                startAnimator(opened, yFloat, startY);
            }
            intercept = false;
        }

        @Override
        public void onDragCancel(Component component, DragInfo dragInfo) {
            if (intercept) {
                startAnimator(opened, yFloat, startY);
                intercept = false;
            }
        }

        @Override
        public boolean onDragPreAccept(Component component, int dragDirection) {
            return intercept;
        }
    };

    private class MyTask extends TimerTask {
        @Override
        public void run() {
            lastOffset = upDateOffset;
        }
    }

    /**
     * 判断是否关闭
     *
     * @return true为关闭 false未关闭
     */
    public boolean isPanelExpanded() {
        return opened;
    }

    /**
     * 弹出PageSlider
     */
    public void expandPanel() {
        isFling = false;
        mViewPager.setVisibility(VISIBLE);
        shapeElement.setRgbColor(RgbColor.fromArgbInt(OPEN_BACKGROUND_COLOR));
        float exY = mViewPager.getContentPositionY();
        startAnimator(true, exY, minY);
        if (stateChangeLisener != null) {
            stateChangeLisener.isOpenState();
        }
    }

    /**
     * 关闭PageSlider
     */
    public void collapsePanel() {
        float collY = mViewPager.getContentPositionY();
        startAnimator(false, collY, maxY);
        if (stateChangeLisener != null) {
            stateChangeLisener.isCloseState();
        }
    }

    /**
     * 获取PageSlider
     *
     * @return PageSlider
     */
    public PageSlider getViewPager() {
        return mViewPager;
    }

    /**
     * 设置PageSlider的适配器
     *
     * @param provider 适配器
     */
    public void setProvider(PageSliderProvider provider) {
        if (this.mViewPager != null) {
            this.mViewPager.setProvider(provider);
        }
    }

    private void startAnimator(boolean open, float startValue, float endValue) {
        if (animator != null) {
            animator.stop();
        }
        float value = (new BigDecimal(String.valueOf(endValue))
                .subtract(new BigDecimal(String.valueOf(startValue)))).floatValue();
        animator = new AnimatorValue();
        animator.setDuration(isFling ? 120 : 300);
        animator.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        animator.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {
            }

            @Override
            public void onStop(Animator anim) {
                opened = open;
                animator = null;
            }

            @Override
            public void onCancel(Animator animator) {
            }

            @Override
            public void onEnd(Animator animator) {
            }

            @Override
            public void onPause(Animator animator) {
            }

            @Override
            public void onResume(Animator animator) {
            }
        });
        animator.setValueUpdateListener((animatorValue, vFloat) -> {
            yFloat = startValue + (value * vFloat);
            mViewPager.setContentPosition(0, yFloat);
            float alpha = (float) (0xC7 * Math.pow((maxY - yFloat) / offsetHeight, 1));
            shapeElement.setAlpha((int) alpha);
        });
        animator.start();
    }

    /**
     * 添加PageSlider
     */
    public void addView() {
        mViewPager.getLayoutConfig().width = LayoutConfig.MATCH_PARENT;
        mViewPager.getLayoutConfig().height = LayoutConfig.MATCH_PARENT;
        addComponent(mViewPager);
        mViewPager.setVisibility(HIDE);
        collapsePanel();
        mViewPager.setBindStateChangedListener(new BindStateChangedListener() {
            @Override
            public void onComponentBoundToWindow(Component component) {
                offsetHeight = component.getHeight();
                offsetAuto = offsetHeight / 2;
            }

            @Override
            public void onComponentUnboundFromWindow(Component component) {
            }
        });
    }

    /**
     * 设置PageSlider状态监听
     *
     * @param stateChangeLisener 监听
     */
    public void setStateChangeLisener(StateChangeLisener stateChangeLisener) {
        this.stateChangeLisener = stateChangeLisener;
    }

    /**
     * 监听接口
     */
    public interface StateChangeLisener {
        void isOpenState();

        void isCloseState();
    }
}